package service

import (
	"context"
	"regexp"
	"time"

	"github.com/go-kratos/kratos/contrib/registry/consul/v2"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/middleware/auth/jwt"
	"github.com/go-kratos/kratos/v2/transport/grpc"
	jwt2 "github.com/golang-jwt/jwt/v5"
	"github.com/hashicorp/consul/api"

	pb "gitee.com/architect-training/substitute-driver/customer-srv/api/customer"
	"gitee.com/architect-training/substitute-driver/customer-srv/api/veri_code"
	"gitee.com/architect-training/substitute-driver/customer-srv/internal/biz"
	"gitee.com/architect-training/substitute-driver/customer-srv/internal/data"
)

const (
	ExpirationTime = 5 // 验证码失效时间
)

type CustomerService struct {
	pb.UnimplementedCustomerServer
	CustomerDao *data.CustomerData
	cb          *biz.CustomerBiz
}

func NewCustomerService(customerDao *data.CustomerData, cb *biz.CustomerBiz) *CustomerService {
	return &CustomerService{CustomerDao: customerDao, cb: cb}
}

func (s *CustomerService) GetVeriCode(ctx context.Context, req *pb.GetVerifyCodeReq) (*pb.GetVerifyCodeResp, error) {
	// 验证电话号码
	pattern := `^(13\d|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18\d|19[0-35-9])\d{8}$`
	reg := regexp.MustCompile(pattern)
	if !reg.MatchString(req.Telephone) {
		// 如果正则匹配失败，则返回错误
		return &pb.GetVerifyCodeResp{
			Code: 400,
			Msg:  "phone number is incorrect",
		}, nil
	}

	consulConfig := api.DefaultConfig()
	consulConfig.Address = "localhost:8500"
	consulClient, err := api.NewClient(consulConfig)
	// 获取服务发现管理器
	dis := consul.New(consulClient)
	if err != nil {
		log.Fatal(err)
	}
	// 连接目标 grpc 服务器
	endpoint := "discovery:///verification-service"
	conn, err := grpc.DialInsecure(
		ctx,
		grpc.WithEndpoint(endpoint),
		grpc.WithDiscovery(dis),
	)

	if err != nil {
		return &pb.GetVerifyCodeResp{
			Code: 500,
			Msg:  "verification service is down",
		}, nil
	}

	// 关闭
	defer func() {
		_ = conn.Close()
	}()

	// 发起请求获取验证码
	client := veri_code.NewVeriCodeClient(conn)
	resp, err := client.GetVeriCode(ctx, &veri_code.GetVeriCodeRequest{
		Length: 6,
		Type:   1,
	})
	if err != nil {
		return &pb.GetVerifyCodeResp{
			Code: 500,
			Msg:  "grpc call of verification code failed",
		}, nil
	}

	err = s.CustomerDao.SetVeriCode(req.Telephone, resp.Code, ExpirationTime)
	if err != nil {
		return &pb.GetVerifyCodeResp{
			Code: 500,
			Msg:  "ops of redis failed",
		}, nil
	}

	return &pb.GetVerifyCodeResp{
		Code:     200,
		Msg:      "OK",
		Data:     resp.Code,
		Duration: ExpirationTime * 60, // 有效期 5 分钟换算成秒
	}, nil
}

func (s *CustomerService) Login(ctx context.Context, req *pb.LoginReq) (*pb.LoginResp, error) {
	// 从 redis 获取验证码
	code := s.CustomerDao.GetVerifyCode(req.Telephone)
	if code == "" || code != req.VerifyCode {
		return &pb.LoginResp{
			Code:    400,
			Message: "验证码错误或失效",
		}, nil
	}

	// 判定电话号码是否注册，来获取顾客信息
	customer, err := s.CustomerDao.GetCustomerByTelephone(req.Telephone)
	if err != nil {
		return &pb.LoginResp{
			Code:    400,
			Message: "顾客信息获取错误",
		}, nil
	}

	token, err := s.CustomerDao.GenerateTokenAndSave(customer, biz.TokenDuration*time.Second, biz.CustomerSecret)
	if err != nil {
		return &pb.LoginResp{
			Code:    500,
			Message: "Token 生成失败",
		}, nil
	}

	// 响应 token
	return &pb.LoginResp{
		Code:          200,
		Message:       "登录成功",
		Token:         token,
		TokenCreateAt: time.Now().Unix(),
		TokenLife:     biz.TokenDuration,
	}, nil
}

// Logout 退出登录
func (s *CustomerService) Logout(ctx context.Context, req *pb.LogoutReq) (*pb.LogoutResp, error) {
	claims, _ := jwt.FromContext(ctx)
	claimsMap := claims.(jwt2.MapClaims)

	if err := s.CustomerDao.DelToken(claimsMap["jti"].(string)); err != nil {
		return &pb.LogoutResp{
			Code:    200,
			Message: "Token删除失败",
		}, nil
	}

	return &pb.LogoutResp{
		Code:    200,
		Message: "logout success",
	}, nil
}

func (s *CustomerService) EstimatePrice(ctx context.Context, req *pb.EstimatePriceReq) (*pb.EstimatePriceResp, error) {
	price, err := s.cb.GetEstimatePrice(req.Origin, req.Destination)
	if err != nil {
		return &pb.EstimatePriceResp{
			Code:    1,
			Message: err.Error(),
		}, nil
	}

	return &pb.EstimatePriceResp{
		Code:        0,
		Message:     "SUCCESS",
		Origin:      req.Origin,
		Destination: req.Destination,
		Price:       price,
	}, nil
}
